/**************************************************************************************
*
*	Copyright (C) 2010 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	EventSystem.cpp
*
*	Comments	-	See EventSystem.h
*
**************************************************************************************/

#include "../Include/EventSystem.h"
#include "../Include/System.h"

namespace Pulse
{
	BOOL EventSystem::AddListener( const EEvent::Type event, const EventListener *pListener )
	{
		ListenerList *pListeners = &m_listeners[event];

		ListenerList::Iterator iter = pListeners->IteratorBegin();
		ListenerList::Iterator iterEnd = pListeners->IteratorEnd();

		// Make sure listener only exist once in the list.
		// We don't want to double trigger the event on the same listener.
		while ( iter != iterEnd )
		{
			if ( *iter == *pListener ) // Already in
				return FALSE;
			++iter;
		}

		pListeners->PushBack( *pListener );

		return TRUE;
	}

	BOOL EventSystem::RemoveListener( const EEvent::Type event, const EventListener *pListener )
	{
		ListenerMap::Iterator findIter = m_listeners.Find( event );

		if ( findIter == m_listeners.IteratorEnd() )
			return FALSE;

		ListenerList *pListeners = &(*findIter).second;

		pListeners->Remove( *pListener );

		return TRUE;
	}

	void EventSystem::TriggerEvent( const IEvent *pEvent )
	{
		ListenerMap::Iterator findIter = m_listeners.Find( pEvent->GetEventType() );

		if ( findIter == m_listeners.IteratorEnd() )
			return;

		ListenerList *pListeners = &(*findIter).second;
		ListenerList::Iterator iter = pListeners->IteratorBegin();
		ListenerList::Iterator iterEnd = pListeners->IteratorEnd();

		// NOTE: Just so you know we can't remove current listener 
		//	while in the middle of triggering the event
		while ( iter != iterEnd )
		{
			(*iter)( (IEvent *)pEvent );
			++iter;
		}
	}

	void EventSystem::ProcessEvents( FLOAT maxTime /*= (FLOAT)INFINITY*/ )
	{
		ULONG currTick = System::GetTickCount();
		ULONG maxMillis = INFINITY;

		// If using maxTime, we'll need to convert it to ticks.
		if ( maxTime != INFINITY )
			maxMillis = currTick + static_cast<ULONG>(1000 * maxTime);
		
		// Swap event queue
		EventQueue *pQueueToProcess1 = &m_eventQueues[m_activeQueue];
		EventQueueUnmanaged *pQueueToProcess2 = &m_eventQueuesUnmanaged[m_activeQueue];
		m_activeQueue = ( m_activeQueue + 1 ) % NUM_QUEUES;

		EventPtr				eventToProcess;
		IEvent					*pEventToProcessUnmanaged;
		ListenerList			*pListeners;
		ListenerList::Iterator	listenerIter;
		ListenerList::Iterator	listenerIterEnd;
		ListenerMap::Iterator	findIter;

		while( pQueueToProcess1->IsEmpty() == FALSE || pQueueToProcess2->IsEmpty() == FALSE )
		{
			if ( pQueueToProcess1->IsEmpty() == FALSE )
			{
				eventToProcess = pQueueToProcess1->Dequeue();

				findIter = m_listeners.Find( eventToProcess->GetEventType() );

				if ( findIter != m_listeners.IteratorEnd() )
				{
					pListeners = &(*findIter).second;

					listenerIter = pListeners->IteratorBegin();
					listenerIterEnd = pListeners->IteratorEnd();

					// TODO: We can paralelize this procedure.
					// For-each registered listener of this event
					while ( listenerIter != listenerIterEnd )
					{
						(*listenerIter)( &*eventToProcess );
						++listenerIter;
					}
				}
			}

			eventToProcess.Reset();

			if ( pQueueToProcess2->IsEmpty() == FALSE )
			{
				pEventToProcessUnmanaged = pQueueToProcess2->Dequeue();

				findIter = m_listeners.Find( pEventToProcessUnmanaged->GetEventType() );

				if ( findIter != m_listeners.IteratorEnd() )
				{
					pListeners = &(*findIter).second;

					listenerIter = pListeners->IteratorBegin();
					listenerIterEnd = pListeners->IteratorEnd();

					// TODO: We can paralelize this procedure.
					// For-each registered listener of this event
					while ( listenerIter != listenerIterEnd )
					{
						(*listenerIter)( pEventToProcessUnmanaged );
						++listenerIter;
					}
				}
			}

			pEventToProcessUnmanaged = PSX_NULL;

			// Check time if we have passed the limit
			if ( maxMillis != INFINITY && System::GetTickCount() > maxMillis )
			{
				// Transfer the remaining events to the next queue to be processed next frame.
				//////////////////////////////////////////////////////////////////////////////

				while ( pQueueToProcess1->IsEmpty() == FALSE )
					PostEvent( pQueueToProcess1->Dequeue() );

				while ( pQueueToProcess2->IsEmpty() == FALSE )
					PostEventUnmanaged( pQueueToProcess2->Dequeue() );

				return;
			}
		}
		
		// DEPRECATED: Old code when we only had one queue to process. Delete this once
		//	there's no more errors in the new code above.
		//int i = 0;
		//// For each event
		//while ( pQueueToProcess1->IsEmpty() == FALSE )
		//{
		//	eventToProcess = pQueueToProcess1->Dequeue();

		//	findIter = m_listeners.Find( eventToProcess->GetEventType() );

		//	if ( findIter != m_listeners.IteratorEnd() )
		//	{
		//		pListeners = &(*findIter).second;

		//		listenerIter = pListeners->IteratorBegin();
		//		listenerIterEnd = pListeners->IteratorEnd();

		//		// TODO: We can paralelize this procedure.
		//		// For-each registered listener of this event
		//		while ( listenerIter != listenerIterEnd )
		//		{
		//			(*listenerIter)( &*eventToProcess );
		//			++listenerIter;
		//		}
		//	}

		//	// Check time if we have passed the limit
		//	if ( maxMillis != INFINITY && System::GetTickCount() > maxMillis )
		//	{
		//		// Transfer the remaining events to the next queue to be processed next frame.
		//		while ( pQueueToProcess1->IsEmpty() == FALSE )
		//			PostEvent( pQueueToProcess1->Dequeue() );

		//		return;
		//	}

		//	++i;

		//}
	}

	//BOOL EventSystem::AbortEvent( const EEvent *pType )
	//{


	//	return TRUE;
	//}

}